package org.pushforward.iwillsurvive.servlets;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Iterator;
import java.util.LinkedHashSet;

import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import org.pushforward.iwillsurvive.data.DatabaseManager;
import org.pushforward.iwillsurvive.data.User;
import org.pushforward.iwillsurvive.helpers.Utils;

/**
 * Servlet implementation class UserManager
 */
@WebServlet(description = "A servlet responsible for handling the user's information", urlPatterns = { "/UserManager" })
@MultipartConfig
public class UserManager extends HttpServlet 
{
	private static final long serialVersionUID = 1L;
	private final String photosDir = "resources/userphotos/";

	private final DatabaseManager db = DatabaseManager.getInstance();
	private LinkedHashSet<User> users = new LinkedHashSet<User>();

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public UserManager() 
	{
		super();
	}

	private void LoadAllUsers() throws SQLException
	{
		String[] loadUserStatement = {"select * from users;"};
		StringBuilder resultSet = new StringBuilder();

		boolean success = db.ExecuteStatements(loadUserStatement, db.GetEmptyParameters(1), resultSet);
		
		if (!success || resultSet.length() == 0)
		{
			throw new SQLException("User grab failure.");
		}

		Utils.Log("Loading users to manager.");
		users.clear();
		String[] usersStrings = resultSet.toString().split("\n");
		for (String userString : usersStrings)
		{
			String[] userData = userString.split(",");
			User user = new User(userData[0], true);
			users.add(user);
		}
	}
	
	private static String getFileExtension(Part part)
	{
		for (String cd : part.getHeader("content-disposition").split(";")) 
		{
			if (cd.trim().startsWith("filename")) 
			{
				String filename = cd.substring(cd.indexOf('=') + 1).trim().replace("\"", "");
				return filename.substring(filename.lastIndexOf('.'));
			}
		}
		return null;
	}

	/**
	 * Saves the given photo data in the user photos dir
	 * @param request the request containing the form data of the photo
	 * @throws IOException
	 * @throws ServletException
	 */
	private void SaveUserPhoto(HttpServletRequest request, User user) throws IOException, ServletException
	{
		Utils.Log("Saving photo for user " + user.Email());
		// grab the part of the form with the photo file data
		Part filePart = request.getPart("file");
		String extension = getFileExtension(filePart);
		Utils.Log("The file has a " + extension + " extension.");

		// delete existing photo (extensions might be different)
		if (user.PhotoSrc() != null)
		{
			Utils.Log("That filename already exists.");
			File photo = new File(getServletContext().getRealPath(this.photosDir + user.PhotoSrc()));
			photo.delete();
			Utils.Log("Previous file deleted.");
		}

		String fileName = user.Email() + extension;
		Utils.Log("New file name is " + fileName);
		InputStream filePartInputStream = filePart.getInputStream();
		byte[] imageBytes = new byte[filePartInputStream.available()];
		filePartInputStream.read(imageBytes);
		String realFilePath = getServletContext().getRealPath(this.photosDir + fileName);
		Utils.Log("Going to write to " + realFilePath);
		File realFile = new File(realFilePath);
		if (!realFile.exists() && !realFile.createNewFile())
		{
			Utils.Log("Couldn't create a file.");
			throw new FileNotFoundException("File doesn't exist and can't create");
		}
		FileOutputStream outputStream = new FileOutputStream(realFilePath);
		Utils.Log("Got image of size " + imageBytes.length + " bytes.");
		outputStream.write(imageBytes);
		outputStream.close();
		
		Utils.Log("Saved photo to file: " + fileName);
		//filePart.write(fileName);
		user.setPhotoSrc(fileName);
		request.getSession().setAttribute("photosrc", fileName);

		user.SaveUserToDB();
	}

	/**
	 * Saves a new post to this user's list
	 * @param request
	 */
	private boolean SavePost(HttpServletRequest request, User user)
	{
		String[] postFields = new String[11];
		postFields[0] = request.getParameter("id");
		postFields[1] = request.getParameter("email");
		postFields[2] = request.getParameter("type");
		postFields[3] = request.getParameter("geotag");
		// convert timestamps from a milisecond notation to JDBC timestamp string
		postFields[4] = new Timestamp(Long.valueOf(request.getParameter("time")).longValue()).toString(); 
		postFields[5] = new Timestamp(Long.valueOf(request.getParameter("timelimit")).longValue()).toString();
		postFields[6] = request.getParameter("capacity");
		postFields[7] = request.getParameter("abilities");
		postFields[8] = request.getParameter("userslimit");
		postFields[9] = request.getParameter("users");
		postFields[10] = request.getParameter("content");

		return user.AddPost(postFields);
	}

	/**
	 * Removes from database a post with the given id
	 * @param idString id of post
	 * @return
	 */
	private boolean RemovePost(String idString, User user)
	{
		Integer id = new Integer(idString);
		return user.RemovePost(id);
	}

	private User GetUser(String email)
	{
		try
		{
			User user = new User(email, false);
			if (!users.contains(user))
			{
				user = new User(email, true);
				users.add(user);
			}
			else
			{
				Iterator<User> iter = users.iterator();
				while (iter.hasNext())
				{
					User user2 = iter.next();
					if (user2.hashCode() == user.hashCode())
					{
						user = user2;
						break;
					}
				}
			}

			Utils.Log("Getting user class for user " + user);
			return user;
		} catch (SQLException e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace();
			return null;
		}
	}

	private void SaveUserAttribute(HttpServletRequest request, User user)
	{
		String attribute = request.getParameter("attribute"); 
		String value = request.getParameter("value");

		if (attribute.equalsIgnoreCase("name"))
		{
			user.setName(value);
			request.getSession().setAttribute("name", value);
		}
		if (attribute.equalsIgnoreCase("password"))
		{
			user.setPassword(value);
		}

		Utils.Log("Changing attribute " + attribute + " to " + value + ".");
		user.SaveUserToDB();
	}

	/**
	 * @param user
	 * @return
	 */
	private boolean DeleteUser(User user)
	{
		if (!user.Delete())
		{
			return false;
		}
		users.remove(user);
		return true;
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{
		User user = GetUser(request.getParameter("email"));
		StringBuilder responseText = new StringBuilder();

		if (user == null)
		{
			responseText.append("error");
			response.setStatus(500);
		}
		else
		{
			switch(request.getParameter("action").toLowerCase())
			{
				case "getposts":
					responseText.append(user.Posts());
					break;
				case "getusers":
					try
					{
						LoadAllUsers();
						Iterator<User> iter = users.iterator();
						while (iter.hasNext())
						{
							responseText.append(iter.next().Email() + "\n");
						}
						responseText.setLength(responseText.length() - 1);
					} catch (SQLException e)
					{
						responseText.append("error");
						System.err.println(e.getMessage());
						e.printStackTrace();
					}
					break;
				default:
					responseText.append("error");
			}
		}

		response.getWriter().write(responseText.toString());
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{
		User user = GetUser(request.getParameter("email"));
		
		if (user == null)
		{
			response.setStatus(500);
			return;
		}

		switch (request.getParameter("action").toLowerCase())
		{
			// handle photo saving
			case "savephoto":
				SaveUserPhoto(request, user);
				response.getWriter().write(user.PhotoSrc());
				break;
			// save new name or password
			case "save":
				SaveUserAttribute(request, user);
				break;
				// save a new post
			case "savepost":
				if (!SavePost(request, user))
				{
					response.setStatus(500);
				}
				break;
				// remove an old post
			case "removepost":
				if (!RemovePost(request.getParameter("id"), user))
				{
					response.setStatus(500);
				}
				break;
			case "deleteuser":
				if (!DeleteUser(user))
				{
					response.setStatus(500);
				}
				break;
			default:
				response.setStatus(500);	
		}
	}

}
